package com.wr.auth.controller;

import cn.dev33.satoken.stp.SaTokenInfo;

import cn.dev33.satoken.stp.StpUtil;
import com.wf.captcha.ArithmeticCaptcha;
import com.wf.captcha.SpecCaptcha;
import com.wr.api.system.service.RemoteSystemService;
import com.wr.auth.common.constants.KeyConstants;
import com.wr.auth.common.service.AuthLogRegService;
import com.wr.auth.common.service.TokenService;
import com.wr.auth.domain.LoginForm;
import com.wr.auth.domain.RegisterForm;
import com.wr.common.core.constants.Constants;
import com.wr.api.system.domain.LoginUser;
import com.wr.common.core.result.R;
import com.wr.common.core.utils.ServletUtils;
import com.wr.common.core.utils.StringUtils;
import com.wr.common.core.utils.encrypt.RsaUtil;
import com.wr.common.core.utils.uuid.IdUtils;
import com.wr.common.log.manager.AsyncManager;
import com.wr.common.log.manager.factory.AsyncFactory;
import com.wr.common.redis.constants.CacheConstants;
import com.wr.common.redis.service.RedisService;
import com.wr.common.security.utils.SecurityUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * @author MyPC
 */
@Tag(name = "A - 认证授权接口")
@RestController
@RequestMapping("/token")
public class TokenController {

    @Autowired
    private TokenService tokenService;

    @Autowired
    private RedisService redisService;

    @Value("${config.captcha}")
    private String captchaType;

    @Value("${sa-token.token-name}")
    private String tokenName;

    @Autowired
    private AuthLogRegService authLogRegService;

    @Autowired
    private RemoteSystemService remoteUserService;

    @Autowired
    private KeyConstants keyConstants;

    @Operation(summary = "获取加密公钥")
    @GetMapping("/getPublicKey")
    public R<Map<String, String>> getPublicKey() {
        String publicKey = "";
        boolean refreshKey = Boolean.parseBoolean(remoteUserService.getConfigInfo("sys:key:generateKey").getData());
        //秘钥uuid
        String rsaUuid = IdUtils.fastSimpleUuid();
        //秘钥缓存键
        String verifyKey = CacheConstants.RSA_LOGIN_KEY + rsaUuid;
        //判断是否生成秘钥对，如果生成，则实时生成秘钥对
        if (refreshKey) {
            RsaUtil.KeyPairInfo keyPairInfo = RsaUtil.getKeyPair();
            redisService.setCacheObject(verifyKey, keyPairInfo.getPrivateKey(), Constants.RSA_KEY_EXPIRATION, TimeUnit.MINUTES);
            publicKey = keyPairInfo.getPublicKey();
        } else {
            //否则读取配置文件中的秘钥对
            publicKey = keyConstants.getPublicKey();
            redisService.setCacheObject(verifyKey, keyConstants.getPrivateKey(), Constants.RSA_KEY_EXPIRATION, TimeUnit.MINUTES);
        }
        Map<String, String> map = new HashMap<>(2);
        map.put("rkId", rsaUuid);
        map.put("publicKey", publicKey);
        return R.ok(map);
    }

    @Operation(summary = "获取登录验证码图片")
    @GetMapping("/captchaImage")
    public R<Map<String, Object>> getCode() {
        Map<String, Object> map = new HashMap<>(2);
        // 保存验证码信息
        String ckId = IdUtils.simpleUuid();
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + ckId;
        String code = null;
        map.put("captchaEnabled", Boolean.parseBoolean(remoteUserService.getConfigInfo("sys:account:captchaEnabled").getData()));
        map.put("ckId", ckId);
        // 生成验证码
        if ("math".equals(captchaType)) {
            ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36, 2);
            code = captcha.text();
            map.put("img", captcha.toBase64());
        } else if ("char".equals(captchaType)) {
            SpecCaptcha captcha = new SpecCaptcha(111, 36, 4);
            code = captcha.text();
            map.put("img", captcha.toBase64());
        }
        redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        return R.ok(map);
    }


    @Operation(summary = "加密登录")
    @PostMapping("/loginKey")
    public R<SaTokenInfo> loginKey(@Valid @RequestBody LoginForm loginForm) {
        String rsaKey = CacheConstants.RSA_LOGIN_KEY + loginForm.getRkId();
        String privateKey = redisService.getCacheObject(rsaKey);
        //删除秘钥
        redisService.deleteObject(rsaKey);
        String pk = RsaUtil.decipher(loginForm.getPk(), privateKey);
        // 用户登录
        LoginUser loginUser = authLogRegService.login(loginForm.getAk(), pk, loginForm.getCk(), loginForm.getCkId());
        // 获取登录token
        return R.ok(tokenService.createToken(loginUser));
    }

    @Operation(summary = "登录")
    @PostMapping("/login")
    public R<SaTokenInfo> login(@Valid @RequestBody LoginForm loginForm) {
        //删除秘钥
        String pk = loginForm.getPk();
        // 用户登录
        LoginUser loginUser = authLogRegService.login(loginForm.getAk(), pk, loginForm.getCk(), loginForm.getCkId());
        // 获取登录token
        return R.ok(tokenService.createToken(loginUser));
    }

    @Operation(summary = "注册")
    @PostMapping("/register")
    public R<SaTokenInfo> register(@Valid @RequestBody RegisterForm registerForm) {
        String rsaKey = CacheConstants.RSA_LOGIN_KEY + registerForm.getRkId();
        String privateKey = redisService.getCacheObject(rsaKey);
        //清除秘钥缓存
        redisService.deleteObject(rsaKey);
        String pk = registerForm.getPk();
        String cpk = registerForm.getCpk();
//        String pk = RsaUtil.decipher(registerForm.getPk(), privateKey);
//        String cpk = RsaUtil.decipher(registerForm.getCpk(), privateKey);
        // 用户注册
        authLogRegService.register(registerForm.getUk(), pk, cpk);
        // 获取登录token
        return R.ok();
    }

    @GetMapping("/getLoginList")
    @Operation(summary = "获取所有在线用户")
    public R<List<LoginUser>> getLoginList() throws InterruptedException {
        Set<String> keys = new HashSet<>(redisService.keys(CacheConstants.LOGIN_USER_KEY + "*"));
        List<LoginUser> loginUsers = new ArrayList<>();
        long currentTime = System.currentTimeMillis();
        for (String key : keys) {
            LoginUser loginUser = redisService.getCacheObject(key);
            loginUsers.add(loginUser);
        }
        return R.ok(loginUsers);
    }

    @Operation(summary = "退出登录")
    @DeleteMapping("/logout")
    public R<?> logout(HttpServletRequest request) {
        String token = ServletUtils.getHeader(request, tokenName);
        if (StringUtils.isNotEmpty(token)) {
            Object loginId = StpUtil.getLoginIdByToken(token);
            if (StringUtils.isNotNull(loginId)) {
                String userKey = CacheConstants.LOGIN_USER_KEY + loginId;
                LoginUser loginUser = redisService.getCacheObject(userKey);
                StpUtil.logout(loginId);
                redisService.deleteObject(userKey);
                // 记录用户退出登录日志
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(loginUser.getAk(), Constants.LOGOUT, "退出登录"));
            }
        }
        return R.ok();
    }
}
